nv.defineSingleton("nv.util.date", function ($this) {
    nv.CreateNamespace("nv.util.date", $this);
    $this.SECONDS_IN_A_DAY = 24 * 3600000;
    var months = new Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
    var fullMonths = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
    var invMonths = nv.array.flip(months);
    $this.formatUTC = $this.FormatUTC = function (formatDate, formatString) {
        if (formatDate instanceof Date) {
            var yyyy = formatDate.getUTCFullYear();
            var yy = yyyy.toString().substring(2);
            var M = formatDate.getUTCMonth() + 1;
            var MM = M < 10 ? "0" + M : M;
            var MMM = months[M - 1];
            var MMMM = fullMonths[M - 1];
            var d = formatDate.getUTCDate();
            var dd = d < 10 ? "0" + d : d;
            var h = formatDate.getUTCHours();
            var hh = h < 10 ? "0" + h : h;
            var m = formatDate.getUTCMinutes();
            var mm = m < 10 ? "0" + m : m;
            var s = formatDate.getUTCSeconds();
            var ss = s < 10 ? "0" + s : s;
            var x = h > 11 ? "PM" : "AM";
            var H = h % 12;
            if (H == 0) {
                H = 12;
            }
            return formatString.replace(/yyyy/g, yyyy).replace(/yy/g, yy).replace(/MMMM/g, "!!!!").replace(/MMM/g, "!!!").replace(/MM/g, MM).replace(/M/g, M).replace(/dd/g, dd).replace(/d/g, d).replace(/hh/g, hh).replace(/h/g, h).replace(/mm/g, mm).replace(/m/g, m).replace(/ss/g, ss).replace(/s/g, s).replace(/!!!!/g, MMMM).replace(/!!!/g, MMM).replace(/H/g, H).replace(/x/g, x);
        } else {
            return "";
        }
    };
    $this.format = $this.Format = function (formatDate, formatString) {
        if (formatDate instanceof Date) {
            var yyyy = formatDate.getFullYear();
            var yy = yyyy.toString().substring(2);
            var M = formatDate.getMonth() + 1;
            var MM = M < 10 ? "0" + M : M;
            var MMM = months[M - 1];
            var MMMM = fullMonths[M - 1];
            var d = formatDate.getDate();
            var dd = d < 10 ? "0" + d : d;
            var h = formatDate.getHours();
            var hh = h < 10 ? "0" + h : h;
            var m = formatDate.getMinutes();
            var mm = m < 10 ? "0" + m : m;
            var s = formatDate.getSeconds();
            var ss = s < 10 ? "0" + s : s;
            var x = h > 11 ? "PM" : "AM";
            var H = h % 12;
            if (H == 0) {
                H = 12;
            }
            return formatString.replace(/yyyy/g, yyyy).replace(/yy/g, yy).replace(/MMMM/g, "!!!!").replace(/MMM/g, "!!!").replace(/MM/g, MM).replace(/M/g, M).replace(/dd/g, dd).replace(/d/g, d).replace(/hh/g, hh).replace(/h/g, h).replace(/mm/g, mm).replace(/m/g, m).replace(/ss/g, ss).replace(/s/g, s).replace(/!!!!/g, MMMM).replace(/!!!/g, MMM).replace(/H/g, H).replace(/x/g, x);
        } else {
            return "";
        }
    };
    $this.parseFromFormat = $this.ParseFromFormat = function (strDate, formatString) {
        var extractPart = function (part) {
            var index = formatString.indexOf(part);
            var result = "";
            if (index > -1) {
                result = strDate.substr(index, part.length);
            }
            return result;
        };
        var parts = [
            ["yyyy", "yy"],
            ["MMM", "MM"],
            ["dd"],
            ["hh"],
            ["mm"],
            ["ss"]
        ];
        var tokens = new Array();
        nv.array.iterate(parts, function (part) {
            var str = "";
            nv.array.iterate(part, function (p) {
                if (!str) {
                    str = extractPart(p);
                    if (str) {
                        tokens[p] = str;
                    }
                }
            });
        });
        nv.array.iterate(tokens, function (k, v) {
            tokens[k] = v.replace(/^0+/, "");
        });
        var d = new Date();
        d.setMonth(0);
        d.setDate(1);
        d.setHours(0);
        d.setMinutes(0);
        d.setSeconds(0);
        var t = 0;
        if (typeof tokens['yyyy'] != "undefined") {
            t = parseInt(tokens['yyyy']);
            if (!isNaN(t)) d.setYear(t);
        } else if (typeof tokens['yy'] != "undefined") {
            t = parseInt(tokens['yy']);
            if (!isNaN(t)) {
                if (t < 20) d.setYear(2000 + t);
                else d.setYear(1900 + t);
            }
        }
        if (typeof tokens['MM'] != "undefined") {
            t = parseInt(tokens['MM']);
            if (!isNaN(t)) d.setMonth(t - 1);
        } else if (typeof tokens['M'] != "undefined") {
            t = parseInt(tokens['M']);
            if (!isNaN(t)) d.setMonth(t - 1);
        } else if (typeof tokens['MMM'] != "undefined") {
            var MMM = nv.String.toProperCase(tokens['MMM']);
            if (typeof invMonths[MMM] != "undefined") {
                d.setMonth(invMonths[MMM]);
            }
        }
        if (typeof tokens['dd'] != "undefined") {
            t = parseInt(tokens['dd']);
            if (!isNaN(t)) d.setDate(t);
        } else if (typeof tokens['d'] != "undefined") {
            t = parseInt(tokens['d']);
            if (!isNaN(t)) d.setDate(t);
        }
        if (typeof tokens['hh'] != "undefined") {
            t = parseInt(tokens['hh']);
            if (!isNaN(t)) d.setHours(t);
        } else if (typeof tokens['h'] != "undefined") {
            t = parseInt(tokens['h']);
            if (!isNaN(t)) d.setHours(t);
        }
        if (typeof tokens['mm'] != "undefined") {
            t = parseInt(tokens['mm']);
            if (!isNaN(t)) d.setMinutes(t);
        } else if (typeof tokens['m'] != "undefined") {
            t = parseInt(tokens['m']);
            if (!isNaN(t)) d.setMinutes(t);
        }
        if (typeof tokens['ss'] != "undefined") {
            t = parseInt(tokens['ss']);
            if (!isNaN(t)) d.setSeconds(t);
        } else if (typeof tokens['s'] != "undefined") {
            t = parseInt(tokens['s']);
            if (!isNaN(t)) d.setSeconds(t);
        }
        return d;
    };
    $this.convertFormat = $this.ConvertFormat = function (strDate, fromFormat, toFormat) {
        return $this.format($this.parseFromFormat(strDate, fromFormat), toFormat);
    };
    $this.now = $this.Now = function () {
        return new Date();
    };
    $this.timestampInSeconds = $this.TimestampInSeconds = function () {
        return Math.floor(new Date().getTime() / 1000);
    };
    $this.formatHuman = function (dateRaw) {
        var date = new Date();
        date.setTime(dateRaw * 1000);
        var now = nv.util.date.Now();
        if (date.getTime() > (now.getTime() - (3600000 * 12))) {
            var msAgo = now.getTime() - date.getTime();
            if (msAgo > 3600000) {
                return Math.round(msAgo / 3600000) + " hrs ago";
            } else if (msAgo > 60000) {
                return Math.round(msAgo / 60000) + " min ago";
            } else {
                var seconds = Math.round(msAgo / 1000);
                if (seconds < 0) {
                    nv.log("nv.util.date.formatHuman got a dateRaw value in the future: " + dateRaw);
                    seconds = 0;
                }
                return seconds + " seconds ago";
            }
        }
        if (date.getFullYear() == now.getFullYear()) {
            return $this.format(date, "MMM d");
        }
        return $this.format(date, "MMM d, yyyy");
    };
});